using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Security;
using System.Windows.Forms;
using WindowsAPI.Service;

namespace WindowsAPI
{
    [SuppressUnmanagedCodeSecurity, Dll(FileName)]
    public static class Netapi32
    {
        public const string FileName = "Netapi32.dll";

        /* ------------------------------------------------------------------------------------------ */

        [DllImport(FileName)]
        public static extern NetError NetShareAdd(
                    [MarshalAs(UnmanagedType.LPWStr)] string strServer,
                    int dwLevel,
                    ref SHARE_INFO_502 buf,
                    out uint parm_err);

        //declare the Netapi32 : NetServerEnum method import

        /// <summary>
        /// Netapi32.dll : The NetServerEnum function lists all servers of the specified type that are visible in a domain. 
        /// For example, an application can call NetServerEnum to list all domain controllers only or all SQL servers only.
        /// You can combine bit masks to list several types. 
        /// For example, a value of 0x00000003  combines the bit masks for SV_TYPE_WORKSTATION (0x00000001) and SV_TYPE_SERVER (0x00000002)
        /// </summary>
        [DllImport(FileName, CharSet = CharSet.Auto, SetLastError = true), SuppressUnmanagedCodeSecurity]
        public static extern int NetServerEnum(
                    string ServerName, // must be null
                    int dwLevel,
                    ref IntPtr pBuf,
                    int dwPrefMaxLen,
                    out int dwEntriesRead,
                    out int dwTotalEntries,
                    int dwServerType,
                    [MarshalAs(UnmanagedType.LPWStr)]
                    string domain, // null for login domain
                    out int dwResumeHandle
                    );

        [DllImport(FileName, CharSet = CharSet.Auto, SetLastError = true), SuppressUnmanagedCodeSecurity]
        public static extern int NetServerEnum(
                    string ServerName,        // must be null
                    int level,        // 100 or 101
                    out IntPtr bufptr,        // pointer to buffer receiving data
                    int prefmaxlen,        // max length of returned data
                    out int entriesread,    // num entries read
                    out int totalentries,    // total servers + workstations
                    uint servertype,        // server type filter
                    [MarshalAs(UnmanagedType.LPWStr)]
                    string domain,        // domain to enumerate
                    IntPtr resume_handle);

        //declare the Netapi32 : NetApiBufferFree method import
        /// <summary>
        /// Netapi32.dll : The NetApiBufferFree function frees the memory that the NetApiBufferAllocate function allocates. 
        /// Call NetApiBufferFree to free the memory that other network management functions return.
        /// </summary>
        [DllImport(FileName, SetLastError = true), SuppressUnmanagedCodeSecurity]
        public static extern int NetApiBufferFree(IntPtr pBuf);

        // <summary>
        /// Uses the DllImport : NetServerEnum
        /// with all its required parameters
        /// (see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/netmgmt/netmgmt/netserverenum.asp
        /// for full details or method signature) to
        /// retrieve a list of domain SV_TYPE_WORKSTATION
        /// and SV_TYPE_SERVER PC's
        /// </summary>
        /// <returns>Arraylist that represents  all the SV_TYPE_WORKSTATION and SV_TYPE_SERVER  PC's in the Domain</returns>
        public static IEnumerable<string> GetNetworkComputers()
        {
            //local fields
            var networkComputers = new List<string>();
            const int MAX_PREFERRED_LENGTH = -1;
            const int SV_TYPE_WORKSTATION = 1;
            const int SV_TYPE_SERVER = 2;
            var buffer = IntPtr.Zero;
            var sizeofINFO = Marshal.SizeOf(typeof(_SERVER_INFO_100));

            try
            {
                //call the DllImport : NetServerEnum 
                //with all its required parameters
                //see http://msdn.microsoft.com/library/
                //default.asp?url=/library/en-us/netmgmt/netmgmt/netserverenum.asp
                //for full details of method signature
                int resHandle;
                int totalEntries;
                int entriesRead;
                var ret = NetServerEnum(
                    null,
                    100,
                    ref buffer,
                    MAX_PREFERRED_LENGTH,
                    out entriesRead,
                    out totalEntries,
                    SV_TYPE_WORKSTATION | SV_TYPE_SERVER,
                    null,
                    out resHandle);

                //if the returned with a NERR_Success 
                //(C++ term), =0 for C#
                if(ret == 0)
                    //loop through all SV_TYPE_WORKSTATION 
                    //and SV_TYPE_SERVER PC's
                    for(var i = 0; i < totalEntries; i++)
                    {
                        //get pointer to, Pointer to the 
                        //buffer that received the data from
                        //the call to NetServerEnum. 
                        //Must ensure to use correct size of 
                        //STRUCTURE to ensure correct 
                        //location in memory is pointed to
                        var tmpBuffer = new IntPtr((int)buffer + (i * sizeofINFO));
                        //Have now got a pointer to the list 
                        //of SV_TYPE_WORKSTATION and 
                        //SV_TYPE_SERVER PC's, which is unmanaged memory
                        //Needs to Marshal data from an 
                        //unmanaged block of memory to a 
                        //managed object, again using 
                        //STRUCTURE to ensure the correct data
                        //is marshalled 
                        var svrInfo = (_SERVER_INFO_100)Marshal.PtrToStructure(tmpBuffer, typeof(_SERVER_INFO_100));

                        //yield return svrInfo.sv100_name;

                        //add the PC names to the ArrayList
                        networkComputers.Add(svrInfo.sv100_name);
                    }
            }
            catch(Exception ex)
            {
                MessageBox.Show(string.Format("Problem with acessing network computers in NetworkBrowser \r\n\r\n\r\n{0}", ex.Message),
                    "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return null;
            }
            finally
            {
                //The NetApiBufferFree function frees 
                //the memory that the 
                //NetApiBufferAllocate function allocates
                NetApiBufferFree(buffer);
            }
            //return entries found
            return networkComputers;
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct _SERVER_INFO_100
    {
        public int sv100_platform_id;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string sv100_name;
    }

    public enum NetError : uint
    {
        NERR_Success = 0,
        NERR_BASE = 2100,
        NERR_UnknownDevDir = (NERR_BASE + 16),
        NERR_DuplicateShare = (NERR_BASE + 18),
        NERR_BufTooSmall = (NERR_BASE + 23),
    }

    public enum SHARE_TYPE : uint
    {
        STYPE_DISKTREE = 0,
        STYPE_PRINTQ = 1,
        STYPE_DEVICE = 2,
        STYPE_IPC = 3,
        STYPE_TEMPORARY = 0x40000000,
        STYPE_SPECIAL = 0x80000000,
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct SHARE_INFO_502
    {
        [MarshalAs(UnmanagedType.LPWStr)]
        public string shi502_netname;
        public SHARE_TYPE shi502_type;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string shi502_remark;
        public int shi502_permissions;
        public int shi502_max_uses;
        public int shi502_current_uses;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string shi502_path;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string shi502_passwd;
        public int shi502_reserved;
        public IntPtr shi502_security_descriptor;
    }
}